/* ir_builder.js--- Build DOM representation for rendering a page in Firemacs

  Copyright (C) 2006 by Lukas Loehrer
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

  Author: Lukas Loehrer <loehrerl [at] gmx [dot] net>
*/

const dom_util = module.require("package", "dom_util");

function constructor(aBrowserService) {
	this._browserService = aBrowserService;
}

function _referenceNode(aNode) {
	return this._browserService.bless(aNode);
}

// {{{DOM visitor interface 

function domVisitElement(elementNode) {
	var elementName = elementNode.tagName.toLowerCase();
	var result = null;

	switch (elementName) {
	case "a":
		result = this._elemAnchor(elementNode);
		break;
	case "h1":
	case "h2":
	case "h3":
	case "h4":
	case "h5":
	case "h6":
		result = this._elemHeading(elementNode);
		break;
	case "img":
		result = this._elemImage(elementNode);
		break;
	case "ul":
		result = this._elemUnorderedList(elementNode);
		break;
	case "ol":
		result = this._elemOrderedList(elementNode);
		break;
	case "li":
		result = this._elemListItem(elementNode);
		break;
	case "form":
		result = this._elemForm(elementNode);
		break;
	case "input":
		result = this._elemInput(elementNode);
		break;
		case "button":
		result = this._elemButton(elementNode);
		break;
		case "select":
		result = this._elemSelect(elementNode);
		break;
case "textarea":
		result = this._elemTextarea(elementNode);
		break;
	case "td":
	case "th":
		result = this._elemTableCell(elementNode);
		break;
	case "table":
		result = this._elemTable(elementNode);
		break;
	case "hr":
	case "br":
		result = this._elemSimpleLeave(elementNode);
		break;
	case "body":
	case "div":
	case "span":
	case "p":
		result = this._elemSimpleInner(elementNode);
		break;
	case "script":
		result = null;
		break;
	default:
		result = dom_util.visitDOMChildNodes(elementNode, this);
	}
	return result;
}

function domVisitText(textNode) {
	if (dom_util.isIgnorableWhitespace(textNode)) {
		return null;
	}
	var text = textNode.nodeValue;
	var cleanText = dom_util.compressWhitespace(text);
	
	var result = {
		type: "text",
		text: cleanText,
		node: this._referenceNode(textNode)
	};
	return result;
}

function domVisitDocument(documentNode) {
	var childNodes = [ dom_util.visitDOMNode(documentNode.body, this) ];

	var result = {
		type: "document",
		title: documentNode.title,
		url: documentNode.URL,
		charset: documentNode.characterSet,
		cn: childNodes
	};
		
	return result;
}

// }}}
// {{{ Functions for different HTML elements

function _setIfAttributePresent(aElement, aAttributeName, aIRElement, aIRName) {
	if (aElement.hasAttribute(aAttributeName)) {
		aIRName = aIRName || aAttributeName;
		aIRElement[aIRName] = aElement.getAttribute(aAttributeName);
	}
}


// A simple inner node of the DOM
function _elemSimpleInner(aElement) {
	var childNodes = dom_util.visitDOMChildNodes(aElement, this);
	var result = {
		type: aElement.nodeName,
		cn: childNodes
	};
	this._setIfAttributePresent(aElement, "id", result);
	return result;
}

function _elemAnchor(anchorElement) {
	var childNodes = dom_util.visitDOMChildNodes(anchorElement, this);
	var result = {
		type: "A",
		node: this._referenceNode(anchorElement),
		cn: childNodes
	};
	if (anchorElement.href) {
		result.href = anchorElement.href;
	}
	if (anchorElement.name) {
		result.name = anchorElement.name;
	}
	return result;
}

function _elemHeading(headingElement) {
	var level = parseInt(headingElement.nodeName.charAt(1));
	var childNodes = dom_util.visitDOMChildNodes(headingElement, this);
	var result = {
		type: "H",
		level: level,
		node: this._referenceNode(headingElement),
		cn: childNodes
	};
	return result;
}

// Simple leaves that cannot be referenced
function _elemSimpleLeave(aElement) {
	var result = {
		type: aElement.nodeName
	};
	return result;
}
// This is a leave of the DOM tree
function _elemImage(imageElement) {
	var desc = imageElement.longDesc || imageElement.alt || "Unlabeled";
	// TODO: Do some more magic here to get a decent description of the image
	var result = {
		type: "IMG",
		desc: desc,
		node: this._referenceNode(imageElement)
	};
	return result;
}


// {{{Lists 

function _elemUnorderedList(listElement) {
	var childNodes = dom_util.visitDOMChildNodesRestricted(listElement, this, [ "LI"]);
	var result = {
		type: "UL",
		cn: childNodes
	};
	return result;
}

function _elemOrderedList(listElement) {
	var childNodes = dom_util.visitDOMChildNodesRestricted(listElement, this, ["LI"]);
	var start = listElement.getAttribute("start");

	var result = {
		type: "OL",
		cn: childNodes
	};
	if (start) {
		result.start = parseInt(start);
	}
	return result;
}

function _elemListItem(itemElement) {
	var childNodes = dom_util.visitDOMChildNodes(itemElement, this);
		var result = {
		type: "LI",
		cn: childNodes
	};
	if ("value" in itemElement) {
		result.value = itemElement.value;
	}
	return result;
}

/* TODO Definition lists */

// }}}

// {{{Table related elements

function _elemTable(aElement) {
	var childNodes = dom_util.visitDOMChildNodes(aElement, this);
	var result = {
		type: "TABLE",
		cn: childNodes
	};
	this._setIfAttributePresent(aElement, "description", result, "desc");
	return result;
}

function _elemTableCell(aElement) {
	var childNodes = dom_util.visitDOMChildNodes(aElement, this);
	var result = {
		type: "TD",
		cn: childNodes
	};
	return result;
}

// }}}
// {{{Form related elements 

function _elemForm(formElement) {
	return dom_util.visitDOMChildNodes(formElement, this);
}

// Input elements are atomic 
function _elemInput(aInputElement) {
	var inputType = aInputElement.type;
	var result = null;
		  
	switch (inputType) {
	case "text":
	case "password":
		result = null; //dom_controls.initInputEntry(aInputElement);
		break;
	case "file":
		result = null;
		break;
	case "radio":
		result = null; //dom_controls.initInputRadio(aInputElement);
		break;
	case "checkbox":
		result = null; //dom_controls.initInputCheckbox(aInputElement);
		break;
	case "submit":
	case "reset":
	case "button":
		result = null; //dom_controls.initInputButton(aInputElement);
		break;
	case "image":
		result = null;
		break;
	case "hidden":
		result = null;
		break;
	default:
		result = null;
	}
	return result;
}

/* This is treated as an atomic object as well */
function _elemSelect(aSelectElement) {
	var result = {
		type: "SELECT",
		options: []
	};
	return result;
}

function _elemTextarea(aTextareaElement) {
	return null;
}

function _elemButton(aButtonElement) {
	return null;
}

// }}}

// }}}

// {{{ Emacs local variables

// local variables:
// folded-file: t
// end:

// }}}
